<?php
namespace App\GameService\games;

use App\Common\GameCommon;
use App\Model\GameType;
use App\Utility\BaseEnum;
use App\Common\AuthCommon;
use Swoole\Timer;
use App\Utility\RedisTools;
use App\Common\BaseComm;
use App\Enum\CardEnum;
use App\Enum\SysEnum;
use App\Utility\DBTools;
use App\Common\PlayCommon;

/**
 * 百人牛牛游戏
 * @author admin
 */
class PlayBaiRen extends BasePlay
{
    protected $timerArr = [
        'prepare' => ['length' => 5],       //准备
        'shuffleCards' => ['length' => 3],  //洗牌
        'dealCards' => ['length' => 3],     //发牌
        'bet' => ['length' => 15],          //下注
        'stopBet' => ['length' => 5],       //停止下注
        'pieAward' => ['length' => 5],      //派奖
        'sleep' => ['length' => 10],        //休息
    ];
    
    protected $betTimer;
    public $historySign = FALSE;
    public static $milleLockKeys = [];

    //事件
    public function __construct()
    {

    }

    /**
     * TODO 开始游戏入口；
     * TODO 必须实现这个方法；
     * TODO 游戏开始逻辑
     */
    public function asycStartGame($gameNo, $roomId, $fjInfo = FALSE)
    { 
        $fds3 = [];
        $fds5 = [];
        $notFdFive = [];
        $messagedata = ["finish"=> 0,"content"=> ""];
        if (!$fjInfo) $fjInfo = BaseComm::GameRoomDetail($roomId);                              //  房间数据；
        $smallMoney = isset($fjInfo["smallPoints"]) ? floatval($fjInfo["smallPoints"]) : 0;     //  房间门槛分；
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        
        //TODO 计算有多少玩家没有下注（清掉）
        if(!empty($wjList)){
            $uidArr = array_column($wjList, 'uid');                 //所有UID用户的分数
            $allPoints = AuthCommon::getUserListPoints($uidArr);   //刷新一下余额
            foreach ($wjList as $rk => $wj) {
                $clientFd = isset($wj['fd']) ? intval($wj['fd']) : 0;
                $currUid = isset($wj['uid']) ? intval($wj['uid']) : 0;
                $isNation = isset($wj['isNation']) ? intval($wj['isNation']) : 0;
                $notBetCount = isset($wj['notBetCount']) ? intval($wj['notBetCount']) :0;
                $curPoints = $allPoints[$currUid] ?? 0;
                
                //TODO 不满足条件的清出游戏列表
                if(!$curPoints || $curPoints <= $smallMoney){
                    unset($wjList[$rk]);
                    //通知当前这个玩家要弹出游戏
                    continue;
                }
                //TODO 最新资金变动更新
                $wjList[$rk]['points'] = $curPoints;
                if($currUid > 0 && $isNation != 3 && $clientFd > 0){
                    if ($notBetCount >= 5){                     //踢出房间
                        @array_push($fds5, $clientFd);
                        @array_push($notFdFive, $currUid);      //要被剔出游戏的玩家
                        continue;
                    }
                    if ($notBetCount == 3){
                        @array_push($fds3, $clientFd);          //提醒下注
                    }
                }else if($isNation === 3 && $curPoints < $smallMoney * 10 ){  //机器人玩家钱不足以满足房间底注时自动充值(按倍数)
                    $newpoints = 0;
                    $res = PlayCommon::addRobotPoints($currUid,$smallMoney,$newpoints);
                    BaseEnum::Logger("机器人加分UID：{$currUid} (记录余额：{$curPoints},门槛分：$smallMoney)  加分后的points={$newpoints} ,操作结果:" . $res, 'user-login');
                    if ($res === TRUE) {
                        $wjList[$rk]['points'] = $newpoints;    //修改可用分
                    }
                }
            }
            //TODO  保存在线玩的列表值
            BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);       
        }
        
        //TODO 未下注提醒
        if (!empty($fds3)){
            $messagedata['finish'] = 0;
            $messagedata['content'] = '您已经连续3局未下注，连续5局未下注将被请离房间';
            BaseEnum::Logger("BRNN-3局未下注 : ({$gameNo} - [{$roomId}]) 玩家FD:" . json_encode($fds3), 'user-login');
            $pushdata = ['m'=>BaseEnum::XYM_0X53,"data"=> $messagedata,"code"=> BaseEnum::MSG_0XE2];
            AuthCommon::pushFrontWeb($fds3, json_encode($pushdata));
        }
        
        //TODO 被踢线提醒
        if (!empty($fds5)){
            $messagedata['finish'] = 1;
            $messagedata['content'] = '您已经连续5局未下注，被请离了游戏桌';
            $pushdata = ['m'=>BaseEnum::XYM_0X53,"data"=> $messagedata,"code"=>BaseEnum::MSG_0XE3];
            if(!empty($notFdFive)){
                //当前玩家被踢出游戏当前游戏
                foreach ($notFdFive as $uid){
                    if(!empty($wjList) && array_key_exists($uid, $wjList)){ unset($wjList[$uid]); }
                }
            }
            $wjList = !empty($wjList) ? $wjList : FALSE ; 
            BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);       //保存在线玩的列表值
            BaseEnum::Logger("BRNN-5局未下注 : ({$gameNo} - [{$roomId}])  踢线玩家FD-UID:" . json_encode([$notFdFive,$fds5]), 'user-login');
            AuthCommon::pushFrontWeb($fds5, json_encode($pushdata));
        }
        
        
        //TODO 　step:1 准备 　〉　洗牌　〉　发牌
        $this->prepare($gameNo, $roomId);
    }
    
    /**
     * TODO 游戏玩家下注实现(下注把倍数存入内存中处理)
     * @param string $gameNo
     * @param int $roomId
     * @param boolean $params
     */
    public function asycPlayBets($roomId,$gameNo,$params = FALSE,& $errorNo = -1){
        $userId = isset($params['loginUid']) ? intval($params['loginUid']) : 0 ;
        $curMiltime = BaseComm::getMillisecond();
        //TODO 放大毫秒值(秒 + 后面三位值)
        $curMilSec = intval(substr("{$curMiltime}", -4)) ;
        //$upMtime = PlayHelp::getInstance()->getLocked($userId,$curMiltime);
        BaseEnum::Logger("百人下注时间 ：{$gameNo} － {$curMiltime} ，当前毫秒末四位 : [ {$curMilSec}] 毫秒) , 上次记录值 ==>" . json_encode(self::$milleLockKeys), 'user-login');
        $defAfter = 100;
        Timer::after($defAfter , function() use ($userId,$roomId,$gameNo,$params,$errorNo) {
            //TODO 下注数据使用内存锁(直接用内存变量操作)
            PlayHelp::getInstance()->addLuck($userId, BaseComm::getMillisecond());
            $seatNo = isset($params['seatNo']) ? intval($params['seatNo']) : 1 ;    //下注位置
            $loginFd = isset($params['loginFd']) ? intval($params['loginFd']) : 0 ; //下注位置
            $qmutil = isset($params['qmutil']) ? intval($params['qmutil']) : 1 ;    //下注倍数
            $chipStr = isset($params['chipNo']) ? $params['chipNo'] : "";           // 其它参数
            if (!$roomId || empty($gameNo) || empty($params) || $userId <= 0) {
                return  FALSE;
            }
            BaseEnum::Logger("执行存锁开始 : [{$gameNo}] - {$chipStr})==TIME：" . BaseComm::getMillisecond(), 'user-login');
            //房間內遊戲数据
            $gameDetail = BaseComm::distMemoryRooms($roomId, FALSE);                // 内存数据；
            $fjStatus = isset($gameDetail['fjStatus']) ? intval($gameDetail['fjStatus']) : 0;
            $userBets = isset($gameDetail['list']) ? $gameDetail['list'] : FALSE;
            BaseEnum::Logger("游戏数据 : (asycPlayBets - [{$gameNo}])==>" . json_encode($gameDetail), 'user-login');
            if (!$gameDetail) {
                $errorNo = BaseEnum::MSG_0XFD;
                return FALSE;
            }
            
            $targetIkey = "seat" . $seatNo;
            //TODO 添加每个位置的限额值[points]
            $seatLimits = GameCommon::getRoomsLimitBets($roomId);       //房间位置限红值
            $seatSize = $seatLimits[$targetIkey] ?? FALSE;
            $seatMaxBet = isset($seatSize["max"]) ? floatval($seatSize["max"]) : 0 ;
            
            if (!isset(self::$milleLockKeys[$userId][$targetIkey])) {
                self::$milleLockKeys[$userId][$targetIkey] = [];
            }
            
            
            /* 
             * //TODO 是否加锁判断
            $getLocked = PlayHelp::getInstance()->getLocked($userId,$gameNo);
            if( $getLocked === TRUE) {
                BaseEnum::Logger("被锁数据，等待解锁 : [{$gameNo}])==>" . json_encode($userBets), 'user-login');
                //等待解锁
                for ($i = 0; $i < 10; $i++) {
                    @usleep(500*1000);
                    if(PlayHelp::getInstance()->getLocked($userId,$gameNo) !== TRUE){
                        $tempArr = BaseComm::distMemoryRooms($roomId, FALSE);                // 内存
                        $userBets = isset($tempArr['list']) ? $tempArr['list'] : FALSE;
                        BaseEnum::Logger("解 锁等待获取新数据， : [{$gameNo}])==>" . json_encode($userBets), 'user-login');
                        break;
                    }
                }
            } */
            
            //TODO 非下注状态返回原始数据
            $returnData = ["s" => 0,"chipNo" => $chipStr,"data" => ["seatNo" => $seatNo,"qmutil"=>  $qmutil]];
            if(isset($fjStatus) &&  $fjStatus === CardEnum::BR_GAME_STEP_2 ){
                //TODO 下注
                //@array_push(self::$milleLockKeys[$userId][$targetIkey], $qmutil); //追加到同一变量中的相同位置
                self::$milleLockKeys[$userId][$targetIkey][$chipStr] = $qmutil;
                $currUser = $userBets[$userId] ?? FALSE;
                if(!$currUser){
                    $currUser = ["seat1"=>0,"seat2"=>0,"seat3"=>0,"seat4"=> 0]; //初始化
                }
                
                //$oneSeatTotal = isset($currUser[$targetIkey])  ? floatval($currUser[$targetIkey]) :  0;
                //$oneSeat_temp =  $oneSeatTotal + $qmutil;
                
                //TODO 总下注分
                $oneSeat_temp = !empty(self::$milleLockKeys[$userId][$targetIkey]) ? array_sum(self::$milleLockKeys[$userId][$targetIkey]) : $qmutil;
                
                //TODO  判断是否超出位最高限额
                if ($seatMaxBet > 0  && $oneSeat_temp > $seatMaxBet) {
                    $errorNo = BaseEnum::MSG_0XE1;                          //超出限额
                    return FALSE;
                }
                $currUser[$targetIkey] = $oneSeat_temp;                     //TODO  下注数据填充=总下注
                $gameDetail['list'][$userId] = $currUser;
                BaseComm::distMemoryRooms($roomId,TRUE,$gameDetail);
                
                //TODO 解锁
                PlayHelp::getInstance()->unLuck($userId);
                BaseEnum::Logger("执行结束 : 解锁 [{$gameNo}])==>" . $chipStr , 'user-login');
                
                //推出来的数据
                $returnData = ["s"=>1,"chipNo" => $chipStr,"data"=>["seatNo" => $seatNo,"qmutil"=>  intval($qmutil) ,"mutdotts" => $oneSeat_temp]];
                BaseEnum::Logger('下注BRNN:执行操作： [' . $gameNo . '--] 执行中.....(当前位：下注' . $qmutil . "，共{$oneSeat_temp})--Totals==" . json_encode(self::$milleLockKeys), 'user-login');
                BaseComm::gameLogger($gameNo,"玩家 {$userId} （位置{$targetIkey}，下注{$qmutil}分）总共 {$oneSeat_temp} 分 ");
            }
            $pushData = ['m'=> BaseEnum::XYM_0X19,"data"=> $returnData];
            AuthCommon::pushFrontWeb($loginFd,json_encode($pushData));
        });
        return TRUE;
    }
    
    
    
    
    /**
     * TODO 玩家进入房间列表时处理
     * @param int $userid
     * @param int $loginFd
     * @param int $roomLists
     */
    public function loopRoomsDetail($userid,$loginFd,$roomLists = FALSE){
        $gameId = 105;
        $gameData = FALSE;
        $oneTimer = null;
        $allUser = BaseComm::distMemoryFjListUser($gameId,FALSE);
        if (!empty($allUser)) {
            $allUser[$userid] = $loginFd;
        }else{
            $allUser = [$userid => $loginFd];
        }
        BaseComm::distMemoryFjListUser($gameId,TRUE,$allUser);   //TODO　写入房间列表FD 
        $gameData = $this -> GameDStore($gameId,$roomLists,TRUE,FALSE);  //获取第一次数据内容 
        
        
        //TODO 开启游戏定时器处理 
        $isTimer = PlayHelp::getInstance()->getResultLog($gameId,"gameTimer");
        
        //TODO 已经启动了就不操作
        if(isset($isTimer) && intval($isTimer) > 0){
            BaseEnum::Logger('玩家进入游戏列表记录FD的操作: USER FD===>'.json_encode([$userid,$loginFd,$isTimer,$allUser]), 'user-login');
        }
        else
        {
            //TODO 只开一个定时器
            BaseEnum::Logger('投递一个任务: 定时执行的任务TaskManager===>'.json_encode([$userid , $loginFd,$isTimer]), 'user-login');
            $oneTimer = Timer::tick(1000, function() use ($roomLists,$gameId , &$oneTimer) {
                
                //TODO 处理FD的情况 ===> 没有玩家停留在房间列表页面 FD
                $allUserFds = BaseComm::distMemoryFjListUser($gameId,FALSE);
                
                BaseEnum::Logger('执行: 定时执行TaskManager===>'.json_encode(["newTimer"=>$oneTimer,"gId"=>$gameId,"fds"=>$allUserFds]), 'user-login');
                if (isset($allUserFds) && !empty($allUserFds)) {
                    BaseEnum::Logger('获取房间列表数据: FDS===>'.json_encode($allUserFds), 'user-login');
                    //TODO 统计数据并推回用户
                    $this -> GameDStore($gameId,$roomLists,FALSE,$allUserFds);
                }else{
                    BaseEnum::Logger("房间列表页的玩家FD无效，定时器（{$oneTimer}）停止 : ROOMS===>".json_encode($roomLists), 'user-login');
                    Timer::clear($oneTimer);
                    PlayHelp::getInstance()->setResultLog($gameId,"gameTimer",FALSE);
                }
            });
            //TODO 定时ID值存入内存中
            PlayHelp::getInstance()->setResultLog($gameId,"gameTimer",$oneTimer);
        }
        return $gameData;
    }
    
    
    /**
     * TODO 获取最新的推送列表数据
     * @param int $gameId
     * @param boolean $roomLists
     * @param boolean $isFirst
     * @param boolean $allUserFds
     * @return array 所有玩家数据内容
     */
    protected function GameDStore($gameId,$roomLists = FALSE,$isFirst = FALSE,$allUserFds = FALSE){
        $gameResult = [];
        $fjField = ['gameId','roomId','roomName','smallPoints','maxInPoints','minPlayer','everyPoints','status'];
        if (!$roomLists) {
            $gameModel = new GameType();
            $gameList = $gameModel->getInfo($gameId,TRUE,$fjField);
            $roomLists = isset($gameList["rooms"]) ? $gameList["rooms"] : FALSE ;    //获取游戏房间列表
        }
        $groomIds  = !empty($roomLists) ? array_column($roomLists, "roomName","roomId") : FALSE;
        $stepNames = CardEnum::GAME_BRNN_STEP ;
        if(!empty($groomIds)){
            //每个房间的数据内容
            foreach ($groomIds as $groomId => $roomName){
                $curStatus = '休眠中';
                $leftTime = - 1;
                //房间数据
                $gameDetail = BaseComm::distMemoryRooms($groomId,FALSE);
                $room_status = isset($gameDetail['fjStatus']) ? intval($gameDetail['fjStatus']) : 0 ;
                $fjStatus = ($room_status<=5) ? $room_status + 1 : 0;                             //默认为下一个状态；
                $gameNo = isset($gameDetail['gameNo']) ? $gameDetail['gameNo'] : FALSE;
                $dealsFlag = isset($gameDetail['dealsFlag']) ? $gameDetail['dealsFlag'] : 0; //结算数据内容
                if ($dealsFlag !== 1 && $room_status === 3) {
                    $fjStatus = CardEnum::BR_GAME_STEP_4;    //特殊处理，没有结算时这里的状态为停止下注
                }
                if (!empty($gameDetail) && !empty($gameNo)) {
                    //BaseEnum::Logger("运行游戏数据：[RoomID={$groomId}] 获取游戏数据要 =>". json_encode($gameDetail),'user-login');
                    $curStatus = isset($stepNames[$fjStatus]) ? $stepNames[$fjStatus] : '休息-';   //房间状态值；
                    $leftTime = PlayHelp::getInstance()->getLoopTim($gameNo);
                }
                
                //TODO  统计和处理房间数据内容；
                $historydata = PlayHelp::getInstance()->getResultLog($groomId,"history");
                $lineData = ["roomId"=>$groomId,"roomName"=>$roomName,"onlineStatus"=> $curStatus,"onlineLeftTime"=> ($leftTime!== FALSE) ? $leftTime : -1,"history"=> []];
                
                //TODO 切割数据
                $historyList = !empty($historydata) ? $historydata : [];                
                if(!empty($historyList)){
                    foreach ($historyList as $r => $onedd){
                        $lineData["history"][] = ["key"=>$r,"data" => $onedd] ;
                    }
                }
                if ($isFirst === true) {
                    //第一次返回所有记录
                    BaseEnum::Logger("历史盈亏：[RoomID={$groomId}] 获取历史开奖数据 =>". json_encode($historydata),'user-login');
                }
                /* $newRes = PlayHelp::getInstance()->getResultLog($gameId,"newhistory_".$groomId);         //最新数据取出来
                if(!empty($newRes) && is_array($newRes) && sizeof($newRes) == 4){      //与上一次记录不一样才推出
                    BaseEnum::Logger("历史盈亏最新数据：[RoomID={$groomId}] 获取历史开奖最新数据 =>". json_encode($newRes),'user-login');
                    PlayHelp::getInstance()->setResultLog($gameId,"newhistory_".$groomId,FALSE);   //取第一个数据
                    $first = $newRes;
                } */
                $gameResult[$groomId] = $lineData ;
            }
            
            //TODO 第一次处理直接返回
            if ($isFirst === TRUE) {
                
                return $gameResult;
            }
            if (!empty($allUserFds)){
                $pushdata = ["m"=>BaseEnum::XYM_0X12,"data"=> $gameResult];         
                AuthCommon::pushFrontWeb($allUserFds,json_encode($pushdata));   //TODO 推送出每个游戏的数据到指定玩家FD上
            }
        }
    }
    
    /**
     * TODO 准备
     * @param $gameNo string 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function prepare($gameNo, $roomId)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['prepare']['length'];
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        $allRecUids = !empty($wjList) ? array_unique(array_column($wjList, "agentId")) : FALSE;

        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_0,"step"=> 1];
        $this->asycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameNo, $roomId,$allRecUids) {
            
            $gameModel = new GameType();
            $gRoomInfo = $gameModel->getGameRoomDetail($roomId); // 获取房间详细参数
            //取实时数据进行操作
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE); 
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_0;
            BaseComm::distMemoryRooms($roomId, true, $fjInfo);  //内存数据；
            $gRoomInfo["users_num"] = 1;
            $gRoomInfo["users"] = [];
            $gRoomInfo["agentsIds"] = $allRecUids ?? FALSE ;     //所有玩家的列表操作值
            
            //房间数据
            $dbEx = GameCommon::createRoomsPlayinfo($roomId, $gameNo, $gRoomInfo);   // 房间落地写入DB
            if(!$dbEx){
                BaseEnum::Logger('PlayBaiRen-(prepare)-RoomId='. $roomId . $gameNo .'-- 游戏开局数据DB失败!!', 'user-login');
                return FALSE;
            }
            BaseEnum::Logger('PlayBaiRen-(prepare)-getROOM ='. $roomId . $gameNo .'--Data'.json_encode($fjInfo), 'user-login');
            
            //TODO 　step:2  洗牌　〉　发牌
            $this->shuffleCards($gameNo, $roomId);
        });
    }

    /**
     * TODO 洗牌
     * @param $gameNo string 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function shuffleCards($gameNo, $roomId)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['shuffleCards']['length'];
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;

        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_1,"step"=> 2];
        $this->asycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameNo, $roomId) {

            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE);//修改状态
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_1;
            $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0;
            
            BaseComm::distMemoryRooms($roomId, true, $fjInfo);
            
            //TODO 提前生成牌数据；
            $this->createCardsList($gameId, $roomId, $gameNo, FALSE);//生成牌型
            
            //TODO 　step:3  　发牌动画
            $this->prepareCards($gameNo, $roomId);
        });
    }

    /** 
     * TODO 按牌发放
     * @param $gameNo string 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function prepareCards($gameNo, $roomId)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['dealCards']['length'];
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_2,"step"=> 3];
        
        
        BaseComm::gameLogger($gameNo,"准备 ->  洗牌 ->　发牌动作执行完   ->  等待下注");
        $this->asycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameNo, $roomId) {
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE);//修改状态
            $firstStamp = isset($fjInfo['create']) ? intval($fjInfo['create']) : 0;
            $countStamp = BaseEnum::getNowTimeLong($firstStamp);
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_2;
            BaseComm::distMemoryRooms($roomId, true, $fjInfo);
            
            BaseComm::gameLogger($gameNo,"开局第{$countStamp}秒     玩家开始下注");
            //TODO  启动下注等待
            $this->waitBetHandel($gameNo, $roomId);
        });
    }

    /**
     * TODO 下注
     * @param $gameNo string 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function waitBetHandel($gameNo, $roomId)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['bet']['length'];

        $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE); //内存数据；
        $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0;
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        
        
        //TODO 异步推送玩家下注金额：（包括总数： 1000 ，每个位置上的增量值--秒级增加）
        $parms = ["gameNo" => $gameNo,"roomId"=>$roomId, "clientIds" => $playerFds,"mutdotts"=>$fjInfo['mutdotts'], "status" => CardEnum::BR_GAME_STEP_3,"step"=> 4];
        $this->betAsycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameId, $gameNo, $roomId) {
            $betrows = [];
            //下注结束操作
            $fjInfo =  BaseComm::distMemoryRooms($roomId,FALSE);
            $fjInfo['fjStatus'] = CardEnum::GAME_STEP_3;            //TODO 更新保存游戏数据
            BaseComm::distMemoryRooms($roomId, TRUE, $fjInfo); 
            
            
            $wjList =  BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
            $firstStamp = isset($fjInfo["create"]) ? intval($fjInfo["create"]) : 0;
            $long = BaseEnum::getNowTimeLong($firstStamp);
            $allPlayerFds = !empty($wjList) ? array_column($wjList, "fd",'uid'):false ;
            
            $betList = isset($fjInfo['list']) ? $fjInfo['list'] : [];
            BaseEnum::Logger("下注截止：BRNN-统计用户下注数据 : " . json_encode($betList), 'user-login');
            
            //TODO 记录玩家不下注的次数(用于3次提醒，5次踢线)
            if(!empty($wjList)){
                foreach ($wjList as &$wj) {
                    $uidval = $wj['uid'] ?? 0;
                    $isNation = $wj['isNation'] ?? 0;
                    $notBet = $wj['notBetCount'] ?? 0;
                    if(!empty($wj) && $uidval > 0 && $isNation != 3){ 
                        $wj['notBetCount']  = $notBet + 1;  //未下注 +1
                    }
                }
                BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);        //保存玩家信息
            }
            
            //TODO 统计所有位置上的下注总额数据
            if (isset($betList) && !empty($betList)){
                $totalSum = 0;
                //格式值：uid => [seat1=>0,seat2=>0,seat3=>0,seat4=>0] 
                foreach ($betList as $userid => $oneBet) {
                    $playDetal = $wjList[$userid] ?? FALSE;
                    $betSum = !empty($oneBet) ? array_sum($oneBet) : 0;  //总下注分：total
                    if ($userid > 0 && !empty($playDetal)) {
                        $playDetal["betmoney"] = $betSum;
                        $playDetal['bet_lists'] = $oneBet;              //下注位详细
                        $betrows[$userid] = $playDetal;
                        $playDetal['notBetCount'] = 0;                  //TODO 有下注把之前连续不下注的置0
                        $wjList[$userid] = $playDetal;
                        
                        $totalSum += $betSum;
                        BaseEnum::Logger("下注玩家UID：{$userid} -总下注分:{$betSum}, 玩家数据： " . json_encode($playDetal), 'user-login');
                    }
                }
                BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);        
                BaseComm::gameLogger($gameNo,"开局第{$long}秒  下注截止 （总共下注 {$totalSum} 分） ");
                
                //TODO 处理玩家的订单-> DB
                $this->createUserOrderFrom($gameNo,$roomId,$betrows);   
            }
            
            //TODO 1、推送牌型数据
            BaseEnum::Logger("发牌推送：BRNN-(bet)-；计算牌型和倍数 start card : " . json_encode($allPlayerFds), 'user-login');
            $seatList = $returndata = FALSE;
            $this -> startCards($gameId,$roomId,$gameNo, $allPlayerFds,$seatList,$returndata);
            
            //延后处理
            Timer::after(200, function() use($gameNo, $roomId, $fjInfo,$seatList,$returndata){
                
                //TODO 2、下注倒计时（同时操作=====>   同时处理要推送牌型数据 ）
                $this->stopBet($gameNo, $roomId, $fjInfo,$seatList,$returndata);
            });
        });
    }

    protected function createUserOrderFrom($gameNo,$roomId,$betPlayers=[]){
        //去取玩家要下注的房间信息
        $playerOrder = [];
        $model = new GameType();
        $fanjianInfo = $model -> getGameTableDetail($gameNo);

        if (empty($betPlayers)){
            $fjInfo = BaseComm::distMemoryRooms($roomId,FALSE);
            $betPlayers = $fjInfo['list'];
        }
        //$wjList = BaseComm::distMemoryPlayer($roomId,FALSE);
        $commonField = array(
            "gameId" => isset($fanjianInfo["gameId"]) ? intval($fanjianInfo["gameId"]) :0 , //游戏类型ID
            "roomId" => isset($fanjianInfo["roomId"]) ? intval($fanjianInfo["roomId"]) :0 , //房间类型ID
            "tableId" => isset($fanjianInfo["id"]) ? intval($fanjianInfo["id"]) :0 ,
            "mainUserId" => 0 , //坐庄家
            "mutleDotts" => 0 , //坐庄家倍数
            "userCount" => count($betPlayers) , //房间下注人数
            "gameNo" => isset($fanjianInfo["gameNo"]) ? trim($fanjianInfo["gameNo"]) :'' , //GameID游戏号
            "gameStartTime" => isset($fanjianInfo["openDateTime"]) ? trim($fanjianInfo["openDateTime"]) :'' , //开始时间
        );
        foreach ($betPlayers as $player){
            if(isset($player['uid']) && intval($player['uid']) > 0){
                $hideName = $player['name']??FALSE;
                $fullName = $player['fullName']??FALSE;
                $curPoints = AuthCommon::getLoginUserPoints($player['uid']); //刷新一下余额
                $orUser = array(
                    'userId' => $player['uid'],
                    'account'=> $fullName ?? $hideName,
                    'cellScore' => 0.0,
                    'chairId' => 0,
                    'channelId' => isset($player['agentId']) ? intval($player['agentId']) : 0,
                    'mainUserId' => 0,
                    'mutleDotts' => 0,
                    'firstPoint' => $curPoints //初始进入游戏分
                );
                $playerOrder[] = array_merge($commonField,$orUser);
            }
        }
        BaseEnum::Logger("生成订单数据：PlayBaiRen-(createUserOrderFrom) => ".json_encode($playerOrder), 'user-login');
        $errorCode = BaseEnum::MSG_0XFB;
        try{
            if(!empty($playerOrder) && intval($fanjianInfo["openStatus"]) === 1){//正常开局 订单
                BaseEnum::Logger('playOrder='. json_encode($playerOrder), 'user-login');
                //$editRows = $model -> editRoomShow($room_No,$editData,"gameNo");
                $tbGOrder = "qc_games_orders";
                $addOrd = DBTools::PoolInsertMultis($tbGOrder,$playerOrder);  //批量插入数据
                if(isset($addOrd) && intval($addOrd) > 0){
                    return TRUE;
                }else{
                    BaseEnum::Logger('百人牛牛下倍数失败:DB异常-RoomNo='. $gameNo, 'user-login');
                    throw new \Exception("百人牛牛下倍数失败:DB异常",$errorCode);
                }
            }else{
                BaseEnum::Logger('百人下倍数失败:组装订单失败! RoomNo='. $gameNo, 'user-login');
                throw new \Exception("百人下倍数失败:组装订单失败!",BaseEnum::MSG_0XF6);  //房间无效
            }

        }catch (\Throwable $e) {
            $error = "Fail:" . $e->getMessage();
            BaseEnum::Logger('出现异常：(updateRoomsPInfo) -- [ rollBackDB ]-RoomId=' . $gameNo . "  ErrorNo = " . $e->getCode() . '-- error==' . $error, 'user-login');
            return $e->getCode();
        } 
        return FALSE;
    }

    
    /**
     * TODO  生成五个位置的牌型信息
     * @param $gameId int
     * @param $roomId int
     * @param $gameNo string 房间编号
     */
    protected function createCardsList($gameId,$roomId,$gameNo) {
        $gameModel = new GameType();                                                            //创建游戏参数
        $gameType = $gameModel ->getList(20);
        $allTypes = !empty($gameType) ? array_column($gameType, "gameKey","gameId") : false ;   // 大类
        $gameUtype = isset($allTypes[$gameId]) ? $allTypes[$gameId] : "";                       //游戏类型
        if(!$gameUtype || !$roomId || !$gameNo) return false;
        $allSeat = [0,1,2,3,4];//5个位置
        $playerNum = sizeof($allSeat);
        BaseEnum::Logger("创建牌 总人数：{$playerNum}人数 >>>--seatinfo=".BaseEnum::JsonEncode($allSeat), 'user-login');
        $seatCards = [];
        //每个FD对应的数据
        $cardList = [];
        $gameJu = substr("{$gameNo}", -10); //取局号后面的10位 统一标准值；
        $gameInning = substr(substr("{$gameNo}", -11),0,1);
        if(!empty($gameUtype) && $gameUtype != "ALL" && !empty($roomId)){
            //创建棋牌数据；
            $cardList = BaseComm::createCards($gameUtype, ["nums"=> $playerNum ,"gameJu"=> $gameJu,"currInning" => intval($gameInning)]);
            GameCommon::checkFirstCards($gameId,$cardList,$roomId);       //TODO 检验是否有预存号码
            
            BaseEnum::Logger("棋牌数据 ：PlayBaiRen-(createCardList)--cards = ".json_encode($cardList), 'user-login');
            if(empty($cardList) || !is_array($cardList)){
                BaseEnum::Logger("生成棋牌数据：PlayBaiRen-(createCardList)- -失败!!!! = ".json_encode([$gameNo,$gameId,$gameUtype]), 'user-login');
                return false;
            }
            //把牌的数据写到DB中====show表,下注才写入DB
            $res = $gameModel->editRoomShow($gameNo,["chessContent" => json_encode($cardList)],"gameNo");
            if(!$res) return false ;
            $gameCards = []; //组装数据[座位号－－－棋牌数据，显示的牌值为单独的牌数据，没有解析数据]
            foreach ($allSeat as $sitidd) {
                $cards = isset($cardList[$sitidd]) ? $cardList[$sitidd] : [];   //取座位号的值：
                if(!$cards) continue ;
                $gameCards[$sitidd] = $cards;
                $oneLine = array("seatNo" => $sitidd ,"cards" => $cards , "dottVal" => -1);
                $seatCards[$sitidd] = $oneLine;
            }
            PlayHelp::getInstance()->setRoomCards($gameNo,$gameCards);         //写入牌到内存数据；
        }
        return $seatCards ;
    }
    
    /**
     * TODO 下注倒计时 －－ 重写
     * @param int $sleepTime
     * @param string $gameNo
     * @param boolean $Params
     * @param boolean $callBackFun
     */
    public function betAsycTimeStart($sleepTime,$gameNo,$Params = FALSE,$callBackFun = FALSE){
        $this->baseTimeLong = $sleepTime;
        if(isset($this->betTimer) && intval($this->betTimer) > 0){
            BaseEnum::Logger("下注定时器销毁：Timer-{$this->betTimer} ,新开倒计时 总共[{$sleepTime}] 秒.....>请等待：" . json_encode($Params), 'user-login');          //推送倒计时显示数
            Timer::clear($this->betTimer); //定时器
        }
        
        //推送数据
        $oneStatus = isset($Params["status"]) ? $Params["status"] : "";
        $roomId = isset($Params["roomId"]) ? $Params["roomId"] : 0;
        $allFds = isset($Params["clientIds"]) ? $Params["clientIds"] : FALSE;
        $stepIndex = (isset($Params["step"]) && intval($Params["step"]) >= 1) ? intval($Params["step"]) : 0;
        $waitSiteNo = (isset($Params["waitSiteNo"]) && intval($Params["waitSiteNo"]) >= 0) ? intval($Params["waitSiteNo"]) : FALSE;
        $gameLun = (isset($Params["gameLun"]) && intval($Params["gameLun"]) >= 1) ? intval($Params["gameLun"]) : 1;
        
        $pushList = ["leftTime" => $sleepTime];
        if($waitSiteNo !== FALSE) {
            $pushList["gLun"] = $gameLun;
            $pushList["waitNo"] = $waitSiteNo;
        }else{
            $pushList["room_status"] = $oneStatus;
        }
        if($stepIndex > 0){
            $pushList["step"] = $stepIndex;
        }
        $pushList["flag"] = TRUE;
        //倒计时的内容放在内存变量中
        PlayHelp::getInstance()->setLoopTim($gameNo,$sleepTime);
        $pushRes = array('m'=> BaseEnum::XYM_0X10 ,"data"=> $pushList);
        AuthCommon::pushFrontWeb($allFds,json_encode($pushRes,JSON_UNESCAPED_UNICODE,JSON_UNESCAPED_SLASHES)) ; //推送
        
        $allUpoints = FALSE;
        //TODO 获取所有玩家的最新分数
        $allRobotUsers = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE); //所有玩家列表
        $allUids = !empty($allRobotUsers) ? array_column($allRobotUsers,"uid") : FALSE; // 获取动态的FD数据
        if (!empty($allUids)) {
            $allUpoints = AuthCommon::getUserListPoints($allUids);
        }
        
        //启用定时器；推送倒计时给前端
        $this->betTimer = Timer::tick(1000, function() use ($roomId,$gameNo,$allFds,$pushList,$callBackFun,$sleepTime,$allUpoints){
            $isFirst = FALSE;
            $myVal = PlayHelp::getInstance()->getLoopTim($gameNo);
            if($myVal == $sleepTime){
                $pushList["flag"] = FALSE;
                $isFirst = TRUE;
            }else{
                $pushList["flag"] = FALSE;
            }
            
            $myVal = $myVal - 1;
            PlayHelp::getInstance()->setLoopTim($gameNo,$myVal);                    // 保存修改后的值
            $loopTimerStop = PlayHelp::getInstance()->getCommTimerStop($gameNo);    //停掉定时器
            if (intval($myVal) <= 0 || $loopTimerStop === true) {                   //特殊情况中断倒计时数据；
                Timer::clear($this->betTimer);
                $this->betTimer = null;
                PlayHelp::getInstance()->setCommTimerStop($gameNo,FALSE);               //unset(Games::$gameCommonTimer[$gameNo]);    //清空数据
                BaseEnum::Logger("定时器工作结束：-(clear)-倒计时结束执行功能： [baseOneTimer]--->>>>loop={$loopTimerStop}----->". $gameNo, 'user-login');
                if($callBackFun !== FALSE){ @call_user_func($callBackFun); }                    //回调方案
            }
            
            $newetRes = [];         //推给前端用户；
            if(!empty($allFds) && $myVal > 0){
                $allRobotUsers = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE); //所有玩家列表
                $allFds2 = !empty($allRobotUsers) ? array_column($allRobotUsers, "fd","uid") : $allFds; // 获取动态的FD数据
                $pushList["leftTime"] = $myVal ;
                

                //TODO 每秒定时器取最新用户FD推送
                $pushRes = array('m'=> BaseEnum::XYM_0X10 ,"data"=> $pushList);
                AuthCommon::pushFrontWeb($allFds2,json_encode($pushRes)) ; //推送
                
                //TODO 1、 统计每个位置的值；
                $fjDetail = BaseComm::distMemoryRooms($roomId,FALSE);
                $betRes = isset($fjDetail['list'])  ? $fjDetail['list'] : [];
                $historyBetRes = isset($fjDetail['mutdotts'])  ? $fjDetail['mutdotts'] : [];
                $roomEveryPots = isset($fjDetail['everyBets'])  ? intval($fjDetail['everyBets']) : 1; //房间底注值
                
                //TODO 2、还要统计每秒种的增量下注数据；
                BaseEnum::Logger("定时统计每秒种的增量下注：-(下注房间：{$roomId} ---{$gameNo} )---->>>>leftTime = {$myVal}----->". json_encode($betRes), 'user-login');
                
                $robot_bets = [];
                
                //TODO 3、机器人列表下注操作(随机下注四个位置的金额)
                $gameChipsArray = [];                
                GameCommon::getRoomsChips($roomId,$gameChipsArray); //机器人获取筹码值
                BaseEnum::Logger("统计筹码概率：--> ". json_encode($gameChipsArray), 'user-login');
                
                if (!empty($allRobotUsers)) {
                    foreach ($allRobotUsers as $oneuid => $oneuuo){
                        if(!isset($oneuuo['isNation']) || (isset($oneuuo['isNation']) && intval($oneuuo['isNation']) != 3)){
                            unset($allRobotUsers[$oneuid]);     //保留机器人玩家
                        }
                    }
                    $robot_num = sizeof($allRobotUsers) ;
                    if(!empty($allRobotUsers) && $robot_num > 0 && $isFirst !== TRUE){   //TODO 每个机器人都下注一定的金额
                        //每个机器人都下注操作
                        foreach ($allRobotUsers as $oneUid => $onePlay) {
                            $currRobots = $betRes[$oneUid] ?? FALSE;
                            $robotNewPoint = $onePlay['points'] ?? 0;
                            if (isset($allUpoints[$oneUid]) && floatval($allUpoints[$oneUid]) > 0) {
                                $robotNewPoint = floatval($allUpoints[$oneUid]);
                            }
                            //TODO 不高于3倍值
                            $maxBup = intval($robotNewPoint / 3);
                            $onebetsAll = !empty($currRobots) ? array_sum($currRobots) : 0;
                            
                            //取随机数据
                            $tempNum1 = PlayCommon::getRandKey($gameChipsArray);
                            $tempNum2 = PlayCommon::getRandKey($gameChipsArray);
                            $tempNum3 = PlayCommon::getRandKey($gameChipsArray);
                            $tempNum4 = PlayCommon::getRandKey($gameChipsArray);
                            //此次下注值：
                            $currBets = [$tempNum1,$tempNum2,$tempNum3,$tempNum4];
                            $onebetsAll += array_sum($currBets);
                            
                            if(!$currRobots){
                                $currRobots = ["seat1"=> 0,"seat2"=> 0,"seat3"=> 0,"seat4"=> 0];
                            }
                            //机器人下注[5-10/每秒钟]不等的下注金额；检测一下不能超过最大剩余积分的一半; 
                            else if($robotNewPoint <= 50 || ($onebetsAll > 0 &&  $onebetsAll  > $maxBup )){
                                continue;
                            }
                            BaseEnum::Logger("玩家[UID={$oneUid},points={$robotNewPoint},最高可投:{$maxBup},下注后总分：{$onebetsAll} ]; (房间：{$roomId} )下注：--> ". json_encode($currBets), 'user-login');
                            
                            //TODO  每个位置上都下注几块钱
                            $currRobots['seat1'] += $tempNum1;   //机器人下注数据
                            $currRobots['seat2'] += $tempNum2;   //机器人下注数据
                            $currRobots['seat3'] += $tempNum3;   //机器人下注数据
                            $currRobots['seat4'] += $tempNum4;   //机器人下注数据
                            $betRes[$oneUid] = $currRobots;
                            $robot_bets[$oneUid] = !empty($currRobots) ? array_sum($currRobots):0; //机器人下注统计
                        }
                        //机器人数据处理一下
                        $fjDetail['list'] = $betRes;
                        BaseComm::distMemoryRooms($roomId,TRUE,$fjDetail); 
                        BaseEnum::Logger("机器人下注计划：-(下注房间：{$roomId},共{$robot_num}机器人)； 统计下注数据-->". json_encode($robot_bets), 'user-login');
                    }
                }
                
                // 所有玩家的下注金额；
                $tongjiRes = $this->sumAllSeatBet($roomId, $betRes,$historyBetRes,$newetRes);
                if(!empty($newetRes) && !empty($tongjiRes)){
                    $fjDetail['mutdotts'] = $newetRes;
                    BaseComm::distMemoryRooms($roomId,TRUE,$fjDetail);
                    //TODO 推送每个位置上的统计数据
                    $pushOrderRes = array('m'=> BaseEnum::XYM_0X52 ,"data"=> $tongjiRes);
                    AuthCommon::pushFrontWeb($allFds2,json_encode($pushOrderRes)) ; //推送
                }
                return ;
            }
        });
    }
    
    
    //统计了所有玩家统计数据
    protected function sumAllSeatBet($roomId,$betRes,$historyBetRes=FALSE,& $newetRes = []){
        // 所有玩家的下注金额；
        $tongjiRes = [
            0 => ["seatNo"=> 1,"total" => 0,"incrfee" => 0],
            1 => ["seatNo"=> 2,"total" => 0,"incrfee" => 0],
            2 => ["seatNo"=> 3,"total" => 0,"incrfee" => 0],
            3 => ["seatNo"=> 4,"total" => 0,"incrfee" => 0],
        ];
        if(!empty($betRes)){
            //每个位置求和值；
            foreach ($tongjiRes as &$oneval){
                $ivkey = isset($oneval['seatNo']) ? intval($oneval['seatNo']) : -1;
                if($ivkey < 0) continue;
                $onekey = 'seat'.$ivkey;    //组合健名
                $histVal = isset($historyBetRes[$onekey]) ? intval($historyBetRes[$onekey]) :0 ; //上一次的统计总分
                $seAlls = array_column($betRes, $onekey);
                $seAllMoney = !empty($seAlls) ? array_sum($seAlls) : 0 ;
                $increVal = 0;
                if ($seAllMoney > 0 && $seAllMoney > $histVal ) {
                    $increVal = $seAllMoney - $histVal;
                    $oneval["incrfee"] = $increVal;                 //增量数据
                }
                $oneval["total"] = $seAllMoney ;
                $newetRes[$onekey] = $seAllMoney;                   //换掉新的值：
            }
        }
        return $tongjiRes;      //TODO 推送每个位置上的统计数据
    }
    
    /**
     * TODO 停止下注
     * @param $gameNo string 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     * @param $useCards array 牌型信息
     */
    public function stopBet($gameNo, $roomId, $fjInfo = FALSE,$seatList=FALSE,$returndata = FALSE)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['stopBet']['length'];

        if (!$fjInfo) {
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE); //内存数据；
        }
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);//全部玩家
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_4,"step"=> 5];
        
        BaseEnum::Logger("停止下注：-(下注房间：{$roomId},{$gameNo}结束，清空) 下注数据-->". json_encode(self::$milleLockKeys), 'user-login');
        
        //下注倒计时
        $this->asycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameNo, $roomId) {
            
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE);//修改状态
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_4;
            BaseComm::distMemoryRooms($roomId, true, $fjInfo);
            
            
            //TODO 派奖开始
            $this->pieAward($gameNo,$roomId, $fjInfo);
        });

        //启动牌值计算数据；====> 看牌过程
        $this->asycWaitDealCenter($roomId,$gameNo,$seatList,$returndata);

    }

    
    /**
     * TODO 触发开五个位置的牌型
     * @param int $gameId
     * @param int $roomId
     * @param string $gameNo
     * @param boolean $playerFds
     * @param boolean $useCards
     * @param boolean $seatList
     * @param boolean $returndata
     */
    public function startCards($gameId,$roomId,$gameNo,$playerFds = FALSE,&$seatList = FALSE,& $returndata = FALSE){
        $fjInfo = BaseComm::distMemoryRooms($roomId,FALSE);
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        BaseEnum::Logger('取内存数据：PlayBaiRen--[StartCards]--->>>' . json_encode($playerFds), 'user-login');
        
        $useCards = [];
        //内存取牌数据：
        $memeryCards = BaseComm::distMemoryCards($gameNo,FALSE);
        if(!empty($memeryCards)){
            foreach ($memeryCards as $setid => $cardval ){
                $useCards[$setid] = array("seatNo" => $setid ,"cards" => $cardval , "dottVal" => -1);
            }
        }
        if(empty($useCards)){
            BaseEnum::Logger("重新启动发牌：PlayBaiRen-(StartCards)-；===={$gameId}==>{$gameNo}| XYM_0X19 --> ==allUserFds : ".json_encode($playerFds), 'user-login');
            $useCards = $this->createCardsList($gameId,$roomId,$gameNo);
        }
        
        BaseEnum::Logger("发牌推送：PlayBaiRen-(StartCards)-；===={$gameId}==>{$gameNo}| XYM_0X19 --> ==data : ".json_encode($useCards), 'user-login');
        
        //发牌
        if(isset($useCards) && !empty($useCards)){
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_4;//修改为发牌成功；
            if(isset($wjList) && !empty($wjList)){
                foreach ($wjList as $uid => $wj) {
                    $wjList[$uid]['pstatus'] = CardEnum::BR_GAME_STEP_4;
                }
            }
            BaseComm::distMemoryRooms($roomId,TRUE,$fjInfo);//保存房间数据
            BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);//保存用户信息
            BaseEnum::wirteRedisData(SysEnum::ONLINE_ROOMS_LIST_KEY.$gameNo,json_encode($fjInfo));      //缓存；
            
            $seatList = $this->processCards($gameId, $roomId, $gameNo,$useCards ,$returndata);               //座位信息
            BaseEnum::Logger('记录发牌结果：PlayBaiRen-(asycStartCards)-发牌-->  ' . json_encode([$returndata,$seatList]), 'user-login');
            if (isset($wjList) && !empty($wjList) && !empty($seatList)){
                $firstStamp = isset($fjInfo["create"]) ? intval($fjInfo["create"]) : 0;
                $long = BaseEnum::getNowTimeLong($firstStamp);
                BaseComm::gameLogger($gameNo,"开局第{$long}秒   比牌结算 ");
                if (!empty($playerFds)){
                    $seatLastArray = array_values($seatList);
                    Timer::after(2000, function() use($gameNo , $playerFds,$seatLastArray){
                        $allrcars = !empty($seatLastArray) ? array_column($seatLastArray, "cards"): [];
                        BaseComm::gameLogger($gameNo,"显示位置牌：". json_encode($allrcars));
                        AuthCommon::pushFrontWeb($playerFds, json_encode(['m' => BaseEnum::XYM_0X17, "data" => $seatLastArray]));
                    });
                }
            }
        }else{
            BaseEnum::Logger('发牌失败：PlayBaiRen-(asycStartCards)-发牌；======>   失败 Fail !! -->  ' . $gameNo, 'user-login');
        }
    }

    /**
     * TODO 异步结算
     * @param $roomId  int      游戏房间ID
     * @param $gameNo string    游戏房间编号
     * @param $seatList array   座位信息
     * @param $returnData array 手牌信息
     * @return $dealRes array   派奖数据
     */
    public function asycWaitDealCenter($roomId,$gameNo,$seatList,$returndata){
        $dealRes = [];
        $tbDisplayName = 'qc_game_display';
        $fjInfo = BaseComm::distMemoryRooms($roomId,FALSE);
        $betList = isset($fjInfo['list']) ? $fjInfo['list'] : [];
        $allbets = isset($fjInfo['mutdotts']) ? $fjInfo['mutdotts'] : [];
        $dealsList = isset($fjInfo['dealRes']) ? $fjInfo['dealRes'] : [];
        
        $dealsList = isset($fjInfo['dealRes']) ? $fjInfo['dealRes'] : [];
        
        //庄家的牌
        $zjCardInfo = isset($seatList['seat0']) ? $seatList['seat0'] : FALSE ;
        $zjCardMutle = isset($zjCardInfo['card_mutle']) ? intval($zjCardInfo['card_mutle']) :1;  
        if(!$zjCardInfo || $zjCardMutle <= 0) return FALSE;
        BaseEnum::Logger('庄家数据：(asycWaitDealCenter)->' . json_encode($zjCardInfo) , 'user-login');
        BaseEnum::Logger('结算前玩家下注：(所有下注数据)->' . json_encode($betList) , 'user-login');
        
       //所有玩家赢的钱（抽水前）
       $allUserWin = $zjUserWin = 0;
       $allTotalFee = !empty($allbets) ? array_sum($allbets) : 0 ; //桌面上的总资金
       
       //TODO 每个位置的输赢
       $allFees = [
           'seat1'=>0,
           'seat2'=>0,
           'seat3'=>0,
           'seat4'=>0,
       ];
        //TODO 结算列表值：所有玩家都统一放在一起
        foreach ($betList as $userid => $betInfo) {
            $realBets = self::$milleLockKeys[$userid] ?? FALSE; //真实下注位置的值 
            if ($userid > 0 && !empty($betInfo)) {
                //TODO 所有的位置亏盈
                $xjMoney = 0;                           //实际盈亏
                $winTotalMoney = 0;                     //总盈亏没抽水算
                $xjWaterMoney = 0;                      //总的抽水
                $eachBetScore = 0;                      //有效下注分
                $allBetScore = array_sum($betInfo);     //有效下注分
                $xjFees = [
                    'seat1'=>0,
                    'seat2'=>0,
                    'seat3'=>0,
                    'seat4'=>0,
                ];
                //TODO 每个位置的输赢
                foreach ($betInfo as $seatNo => $bet) {
                    $xjWater = 0;
                    $winPrice = 0;
                    $tempRealMoney = 0; //实际赢输
                    $eachScore = 0;     //有效下注
                    $seatinfo = isset($seatList[$seatNo]) ? $seatList[$seatNo] : false;                     //座位输赢状态
                    $currSeatMutle = isset($seatinfo['card_mutle']) ? intval($seatinfo['card_mutle']) :1;   //牌型倍数
                    
                    //TODO 替换掉真实的下注分
                    if (isset($realBets[$seatNo]) && !empty($realBets[$seatNo])) {
                        $bet = is_array($realBets[$seatNo]) ? array_sum($realBets[$seatNo]) : $bet ; //真实下注分数
                    }
                    
                    //TODO 单独计算庄家赢的 FEE
                    if (isset($seatinfo) && !empty($seatinfo) && $bet > 0) {
                        //TODO 比庄 ---> 赢
                        if (isset($seatinfo['if_win']) && intval($seatinfo['if_win']) == 1) {
                            $winPrice = bcmul(floatval($bet),$currSeatMutle,3);                             //闲赢的分数
                            $xjWater = bcmul($winPrice, BaseComm::getSetWaterDott(), 3);                    //最后计算抽水的分；
                            $tempRealMoney = bcsub($winPrice, $xjWater,2);                                  //TODO 有效下注 =  下注分 - 抽水额度
                            //$zjUserWin = bcsub($zjUserWin, $winPrice, 2);                                 //庄家输的
                            $allUserWin = bcadd($allUserWin, $winPrice,2);                                  //所有玩家赢走的钱
                            $xjWaterMoney = bcadd($xjWaterMoney, $xjWater, 2);
                            $eachScore = $winPrice ?? 0;
                        } else {
                            //TODO 比庄 ---> 输
                            $winPrice = 0 - bcmul(floatval($bet),$zjCardMutle,3);                             //闲输的分数
                            $tempRealMoney = $winPrice;                                                       //输的情况:庄家赢，要用庄家的倍数计算（玩家下注的钱）
                            //$zjUserWin = bcadd($zjUserWin, $betPrice, 2);                                   //庄家赢的
                            $eachScore = floatval(abs($winPrice));                                            //TODO  有效下注 =  参与的下注分
                        }
                        
                        $xjFees[$seatNo] = $winPrice;           //TODO 闲家输赢位置(不考虑抽水)
                        
                        //组合数据
                        $eachBetScore = bcadd($eachBetScore, $eachScore, 2);        //TODO 有效下注总额
                        $xjMoney = bcadd($xjMoney, $tempRealMoney, 2);              //TODO 玩家实际盈亏值（+ / -都有）
                        $winTotalMoney = bcadd($winTotalMoney, $winPrice, 2);       //TODO 玩家总盈亏值（+ / -都有）
                        
                        //TODO ALL每个位置的输赢情况
                        $oldFeeValue = $allFees[$seatNo] ?? 0 ;
                        $allFees[$seatNo] = $oldFeeValue ? bcadd($oldFeeValue,$winPrice , 2) : $winPrice;      
                    }
                }
                
                //TODO 单个用户 === 结算总金额
                $dealRes[$userid] = array(
                    "uid" => $userid,
                    'seatNo' => 0,
                    "money" =>      floatval($winTotalMoney),           // 总输赢（不除抽水）
                    "cellScore" =>  $eachBetScore,                       // 有效下注（取所有位置的下注总和）＝＝＝ 下注多少就多少
                    "eachprice" =>  $eachBetScore,                      // 有效下注（取所有位置的下注总和）
                    "allBetScore" => $eachBetScore,                      // 所有位下注总分
                    "lastPoints"=>  floatval($xjMoney),                 // 实际输赢（除去水钱）
                    'water' =>      $xjWaterMoney,
                    "cards" =>      $betInfo,
                    "seatFees" =>   $xjFees
                );
            }
        }
        $flag = FALSE;
        $tempValues = [];
        $realUserNotices = [];
        $whereOpt = array("roomId"=> $roomId,"gameNo"=> $gameNo);
        self::$milleLockKeys = []; //TODO 清空数据
        if(!empty($dealRes) && !empty($returndata) && isset($returndata['cardvalues']) && !empty($returndata['cardvalues'])) {
            //结算订单数据；
            BaseEnum::Logger('结算数据：(dealUserOrder---Result)===-->' . json_encode(['uids'=>array_keys($dealRes)]), 'user-login');
            //操作相关的注单信息；1、修改订单状态，补全订单信息； 2、用户资金交易数据处理； 3、收拾桌面数据，匹配列表清空；
            $result = GameCommon::dealUserOrder($roomId,$gameNo,$dealRes,$returndata);
            BaseEnum::Logger('结算输出结果：(dealUserOrder---Result)===-->' . json_encode($result), 'user-login');
            
            $agentIds = [];
            //操作数据 ＝＝＝＝ 组装成 WEB端要用的数据；
            if(!empty($result) && sizeof($result) > 0){ //结算成功；
                foreach ($dealRes as $r => $dealobj){
                    $oneuid = isset($dealobj['uid']) ? intval($dealobj['uid']):0 ;
                    $oneArr = isset($result[$oneuid]) ? $result[$oneuid]:FALSE ;
                    if(!empty($oneArr) && isset($oneArr['points'])){
                        $oneAgid = $oneArr['agentId'] ?? 0;                             //上级代理ID
                        $isNation = $oneArr['isNation'] ?? 0;
                        $uuuName = $oneArr['userName'] ?? "";
                        if ($isNation !== 3) {
                            $tempValues[] = "玩家{$uuuName}  输赢分： " . floatval($oneArr['changeMoney']) ;
                            //TODO 游戏的真实玩家结算数据
                            $realUserNotices[$oneuid] = ["userName"=> $oneArr['userName'] ?? "", "roomId" => $roomId, "changeMoney"=>floatval($oneArr['changeMoney'])];
                        }
                            
                        $dealRes[$r]["agentId"] = $oneAgid;
                        $dealRes[$r]["userName"] = $oneArr['userName'] ?? "";           //玩家用户名
                        $dealRes[$r]["points"] = floatval($oneArr['points']);           //修改用户真实余额值
                        $dealRes[$r]["changeMoney"] = floatval($oneArr['changeMoney']);
                        if(!$agentIds || (!empty($agentIds) && !in_array($oneAgid, $agentIds))) {
                            $agentIds[] = $oneAgid;
                        }
                    }
                }
                //把牌局信息补全；
                $editDisplay = [
                    "agentsIds" => !empty($agentIds) ? implode(',', $agentIds) :"",
                    "playerInfo"=> json_encode($dealRes,JSON_FORCE_OBJECT),
                    "finishDateTime"=> BaseEnum::getNowTime(true),
                    "openStatus" => CardEnum::BR_GAME_STEP_5
                ];
                $reds =  DBTools::PoolUpdate($tbDisplayName,$editDisplay,$whereOpt);   //连接池更新操作
                if(isset($reds) && intval($reds) >0){
                    $flag = TRUE;
                    BaseEnum::Logger('结算完成：(dealPlayMoney)===补全牌局信息-->res:'.$reds. "==== " . json_encode($whereOpt), 'user-login');
                }
            }
        }elseif(empty($dealRes)){
            BaseEnum::Logger('结算完成：(asycWaitDealCenter)===下注玩家为空'.'$dealRes'. "==== " . json_encode($dealRes), 'user-login');
            //把牌局信息补全；
            $editDisplay = ["playerInfo"=> json_encode([],JSON_FORCE_OBJECT),"finishDateTime"=> BaseEnum::getNowTime(true),"openStatus" => CardEnum::BR_GAME_STEP_5];
            $reds =  DBTools::PoolUpdate($tbDisplayName,$editDisplay,$whereOpt);   //连接池更新操作
            $flag = TRUE;
        }else{
            BaseEnum::Logger('结算失败：(asycWaitDealCenter)===传参失败，玩家手牌信息为空:', 'user-login');
        }
        
        //TODO 操作数据 ＝＝＝＝ 组装成 WEB端要用的数据（用户余额值 ）；
        if ($flag === TRUE){
            foreach ($dealRes as $uid => $dealInfo) {
                $mySFee = [];
                $Fees = $dealInfo['seatFees'] ?? FALSE; 
                if (isset($dealInfo['money'])) {
                    if(!empty($Fees)){
                        foreach ($Fees  as $myset1 => $mony2){  $mySFee[] = ['seatNo' => $myset1,"money"=> floatval($mony2)]; }
                    }
                    //TODO 下注结果写内存
                    $dealsList[$uid] = array(
                        'points' => $dealInfo['points'] ?? 0,
                        'changeMoney' => $dealInfo['changeMoney']??0 ,
                        'money' => floatval($dealInfo['money']),
                        "lists" => $mySFee
                    ); 
                }
            }
            //TODO 每个位置的输赢
            $allFeesList = [];
            if(!empty($allFees)){
                foreach ($allFees  as $myset => $mony1){
                    $allFeesList[] = ['seatNo' => $myset,"money"=> floatval($mony1)]; 
                }
            }
            $fjInfo['dealRes'] = $dealsList;
            $fjInfo['dealMain'] = ["totalFee" => $allTotalFee ,"mainFee"=> floatval($zjUserWin),"seatList" => $allFeesList ,"mainFlag"=> ($zjUserWin > 0) ? TRUE : FALSE];     //庄家输赢状态  ==> 更新用户的输赢信息；
            BaseEnum::Logger('百人牛牛结算处理：BRNN-(asycWaitDealCenter)-；======> SUCCESS', 'user-login');
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_4; 
            $fjInfo['dealsFlag'] = 1; //标识结算完成
            $firstStamp = isset($fjInfo['create']) ? intval($fjInfo['create']) : 0;
            $countStamp = BaseEnum::getNowTimeLong($firstStamp);
            
            
            BaseComm::distMemoryRooms($roomId,TRUE,$fjInfo);
            BaseComm::gameLogger($gameNo,"开局第{$countStamp}秒   结算完成");
            PlayCommon::gameNoticeSaveCache(105,$gameNo,$realUserNotices);  //TODO 加入游戏通知列表
            Timer::after(2000, function() use($gameNo,$tempValues){
                $temp_str = !empty($tempValues) ? implode('\r\n' , $tempValues) : "暂无数据";
                BaseComm::gameLogger($gameNo,"结算数据：" . $temp_str,["finish"=>TRUE]);
            });
        }
        return $dealRes;
    }

    protected function processCards($gameId,$roomId, $gameNo, $useCards = FALSE,&$returndata = false)
    {
        $gameModel = new GameType();
        $roomDetail = $gameModel->getGameRoomDetail($roomId); //房间信息
        $gameUtype = isset($roomDetail['gpKey']) ? $roomDetail['gpKey'] : "";  //大类别名
        //BaseEnum::Logger('取内存数据：PlayBaiRen-(BaseDB::gameRoomPlayerData)-[asycStartCards]--->>>' . json_encode($fjInfo), 'user-login');
        //启动发牌；======> 操作代码 ： XYM_0X19；
        if (empty($useCards)) {
            BaseEnum::Logger("生成牌型：PlayBaiRen-(asycStartCards)-；===={$roomId}==>{$gameNo}",'user-login');
            $useCards = $this->createCardsList($gameId, $roomId, $gameNo);
        } else {
            BaseEnum::Logger("发牌推送：PlayBaiRen-(asycStartCards)-；===={$roomId}==>{$gameNo}| XYM_0X19 --> ==data : " . json_encode($useCards), 'user-login');
        }
        //zjObject庄家牌型，useCards闲家牌型
        foreach ($useCards as $seatId => $cardinfo) {
            if ($seatId == 0  && !empty($cardinfo)) { 
                $zjObject = $useCards[$seatId];
                unset($useCards[$seatId]);                                         //庄家牌内容
            }
        }
        $resultData = BaseComm::dealCards($gameUtype,$zjObject,$useCards,$roomDetail,$returndata);
        return $resultData;
    }

    /**
     * TODO 派奖
     * @param $gameNo int 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function pieAward($gameNo,$roomId,$fjInfo = FALSE)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['pieAward']['length'];
        if (!$fjInfo) {
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE); //内存数据；
        }
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_5,"step"=> 6];
        
        // TODO 派奖倒计时结束时派数据
        $this->asycTimeStart($sleepTime, $gameNo, $parms, function () use ($gameNo, $roomId,$playerFds) {
            
            $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
            $allWjFds = !empty($wjList) ? array_column($wjList, "fd","uid") : FALSE ;
            
            $fjStat = -1;
            $fjInfo = $dealsList = $dealsMain = FALSE;
            
            // TODO 计算完就数据库订单结算成功
            $init_timelong = 60;
            while ($fjStat != CardEnum::BR_GAME_STEP_4){    //TODO 获取实时房间内的状态
                $init_timelong --;                          //TODO 结算等待最长1分钟
                $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE);
                $dealsList = isset($fjInfo['dealRes']) ? $fjInfo['dealRes'] : [];
                $dealsMain = isset($fjInfo['dealMain']) ? $fjInfo['dealMain'] : FALSE; // 庄家输赢
                $fjStat = $fjInfo['fjStatus'] ?? -1;
                BaseEnum::Logger("百人牛牛内存数据：BRNN-(pieAward) ===>庄家位： ".json_encode($dealsMain), 'user-login');
                BaseEnum::Logger("百人牛牛派奖中：BRNN-(pieAward) start===[status = {$fjStat}]===> ".json_encode($dealsList), 'user-login');
                if($fjStat === CardEnum::BR_GAME_STEP_4) { 
                    break;
                }
                if ($init_timelong <= 0) break;
                @sleep(1);
            }
            
            //TODO 只获取前20人的余额
            $frontList = [];
            if (!empty($wjList)) {
                $frontList = array_column($wjList,'points',"uid") ;
                /* @rsort($frontList,SORT_NUMERIC);    //降序排列
                if(isset($frontList) && !empty($frontList) && sizeof($frontList) > 20){
                    $frontList = array_slice($frontList, 0,20);             //取前二十名玩家信息
                } */
            }
            $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_5;                 //派奖
            BaseComm::distMemoryRooms($roomId, true, $fjInfo);
            
            //$totalFee = isset($dealsMain["totalFee"]) ? floatval($dealsMain["totalFee"]) : 0 ;  //所有桌面分
            //$mainFee = isset($dealsMain["mainFee"]) ? floatval($dealsMain["mainFee"]) : 0 ;     //所有桌面分
            //TODO 结算推送结果===USER ORDER
            
            $playWins = ["deals" => $dealsMain,"player" =>FALSE ,'fronts'=> $frontList ?? FALSE];   
            if(!empty($wjList)){
                foreach ($wjList as $oneuid => $wj) {
                    if(!empty($wj) && isset($wj['fd']) && intval($wj['fd']) > 0) {
                        $popMoney = isset($wj['points']) ? floatval($wj['points']) :0.0;        //实际分数
                        $dealinfo = isset($dealsList[$oneuid]) ? $dealsList[$oneuid] : FALSE ;
                        if ($oneuid > 0 && !empty($dealinfo)) {                                 //玩家没有下注也要推送结算数据
                            $playWins["player"] = $dealinfo;
                            $wjList[$oneuid]['points'] = $dealinfo['points'] ?? 0.0;            //实际分数更新
                        }else{
                            $playWins["player"] = ["changeMoney" => 0,"money"=> 0,"points"=> $popMoney];
                        }
                        AuthCommon::pushFrontWeb($wj['fd'],json_encode(['m'=> BaseEnum::XYM_0X1C,"data"=> $playWins]));
                    }
                }
                BaseComm::distMemoryPlayer($roomId,TRUE,$wjList,TRUE);                          //更新玩家列表
            }
            
            //TODO 开奖结果走势数据推到前端
            $historyWins = PlayHelp::getInstance()->getResultLog($roomId,'history');
            if(!empty($historyWins)){
                $WinsList = [];
                foreach ($historyWins as $r => $oneivs){
                    $WinsList[] = ["key"=>$r,"data"=> $oneivs];
                }
                $oneHistory = ["gameNo"=>$gameNo,"roomId"=> $roomId ,"history" => $WinsList];    //只推增量数据；
                $pushdata = ['m'=> BaseEnum::XYM_0X51,"data" => $oneHistory];
                AuthCommon::pushFrontWeb($allWjFds,json_encode($pushdata));
            }
            
            //TODO 进入休息时间段
            Timer::after(1000, function()use($gameNo,$roomId,$fjInfo) {
                $this->sleep($gameNo,$roomId,$fjInfo);
            });
        });

    }

    /**
     * 休息时间生成下局房间信息
     * @param $gameNo int 房间编号
     * @param $roomId int 房间列表ID
     * @param $fjInfo array 房间信息
     */
    public function sleep($gameNo,$roomId, $fjInfo = FALSE)
    {
        $timerArr = $this->timerArr;
        $sleepTime = $timerArr['sleep']['length'];
        if (!$fjInfo) {
            $fjInfo = BaseComm::distMemoryRooms($roomId, FALSE); //内存数据；
        }
        $fjInfo['fjStatus'] = CardEnum::BR_GAME_STEP_6;                 //休息中
        $gameId = isset($fjInfo['gameId']) ? intval($fjInfo['gameId']) : 0;
        $gameNo = isset($fjInfo['gameNo']) ? intval($fjInfo['gameNo']) : 0;
        $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
        $playerFds = !empty($wjList) ? array_column($wjList, "fd", "uid") : false;
        $parms = ["gameNo" => $gameNo, "clientIds" => $playerFds, "status" => CardEnum::BR_GAME_STEP_6,"step"=> 7];
        BaseComm::distMemoryRooms($roomId, TRUE, $fjInfo);
        $this->asycTimeStart($sleepTime, $roomId, $parms, function () use ($gameId, $roomId) {
            $gameNo = BaseComm::getRoomUnioxNo($roomId);        //生成房间编号
            $fjInfo = array(                                    //组装房间的状态信息;
                "isOpen" => 1,
                "gameId" => $gameId,
                'grid' => $roomId,
                "gameNo" => $gameNo,
                "fjStatus" => CardEnum::BR_GAME_STEP_0,
                "mutdotts" => [
                    'seat1' => 0,
                    'seat2' => 0,
                    'seat3' => 0,
                    'seat4' => 0,
                ],
                'create' => time(),
                'list' => [],
            );
            BaseComm::distMemoryRooms($roomId, TRUE, $fjInfo);//写入下一局房间信息
            RedisTools::Pool_set(SysEnum::ONLINE_ROOMS_LIST_KEY . $roomId, json_encode($fjInfo), 7200);         //缓存游戏信息；
            BaseEnum::Logger("结束：PlayBaiRen-(asycStartCards)；===={$roomId} " . json_encode($fjInfo), 'user-login');
            
            
            //TODO 游戏重新开始进入下一局            
            Timer::after(3000, function() use($gameNo,$roomId, $fjInfo){
                
                //TODO 游戏重新开始 ： 游戏重新开始只更新开奖期号
                $wjList = BaseComm::distMemoryPlayer($roomId,FALSE,null,TRUE);
                $allWjFds = !empty($wjList) ? array_column($wjList, "fd","uid") : FALSE ;
                $pushdata = ['m'=> BaseEnum::XYM_0X55,"data" => ["gameNo"=>$gameNo,"gstart"=> 2 ,"status" => -1 ]];
                AuthCommon::pushFrontWeb($allWjFds,BaseEnum::JsonEncode($pushdata));
                
                //TODO　之前步骤重新开始
                $this->asycStartGame($gameNo,$roomId, $fjInfo);
            });
        });
    }
    
    /**
     * 玩家退出游戏列表
     * @param int $gameRoomId   房间ID值
     * @param boolean $userId   玩家UID
     */
    public static function doGameOutPlay($gameRoomId,$userId = FALSE){
        $allPlayer = BaseComm::distMemoryPlayer($gameRoomId,FALSE,null,TRUE);
        if (isset($allPlayer[$userId])) {
            unset($allPlayer[$userId]); //游戏ID
            $allPlayer = !empty($allPlayer) ? $allPlayer : FALSE ;
            BaseComm::distMemoryPlayer($gameRoomId,TRUE,$allPlayer,TRUE);
            //玩家列表数据更新
            $redisobj = new RedisTools();
            $redisobj->select(0);
            $redisobj->hDel(SysEnum::USER_PLAY_FDMAP_KEY, $userId); // 退出房间；清掉玩家数据；
        }
    }

    /**
     * 结算
     * @param array $zjdata 庄家数据，0号位数据
     * @param array $xjdata 闲家数据，1-4位数据
     * @param array $fjdata 房间详情
     * @param array $returndata 手牌信息
     * @return $resultdata 座位信息
     */
    public static function dealRoomGameBRNN($zjdata = false, $xjdata = false, $fjdata = false,&$returndata = false)
    {
        $resultdata = FALSE;
        $allCards = [];
        $seatResult = [];
        $gameId = isset($fjdata['gameId']) ? intval($fjdata['gameId']) : 0;
        $roomId = isset($fjdata['roomId']) ? intval($fjdata['roomId']) : 0;
        BaseEnum::Logger("结算BRNN： ING ---- " . json_encode($zjdata), 'user-login');
        $zjNiuLevel = 0; //比牌操作；庄家跟每个闲家一一对比；
        $zjcode = isset($zjdata['cards']) ? $zjdata['cards'] : false;                   //庄家的牌数据；
        $zjSeatNo = isset($zjdata['seatNo']) ? intval($zjdata['seatNo']) : 0;           //庄家座位号；
        $gameUtype = isset($fjdata["gpKey"]) ? $fjdata["gpKey"] : "";                       //游戏类型
        $fjSeat = 5;//房间位置
        $zjRes = CardEnum::dealNiuNiuLevel($zjcode, $zjNiuLevel);
        if (!empty($zjdata) && !empty($xjdata) && sizeof($xjdata) > 0 && !empty($zjRes) && $zjSeatNo >= 0) {
            $resultMul = []; //每个位置庄闲输赢倍数
            $zjCardmutl = isset($zjRes['mutl_value']) ? $zjRes['mutl_value'] : 1;     //牌型倍数
            if (isset($zjRes['niuGroups']) && !empty($zjRes['niuGroups'])) {
                $zjcode = BaseEnum::changeGroupArray($zjcode, $zjRes['niuGroups']);
            }
            $zjRes['cards'] = $zjcode;
            $allCards = [$zjSeatNo => $zjcode];
            $resultdata["seat{$zjSeatNo}"] = array('seatNo'=>$zjSeatNo,'cards'=>$zjcode,'level'=>$zjNiuLevel,'card_mutle'=>$zjCardmutl);
            foreach ($xjdata as $xjinfo) {
                $xjLevel = -1;   //闲家牛等级
                $xjMoney = 0;   //闲家赢的钱

                $xianjCard = isset($xjinfo['cards']) ? $xjinfo['cards'] : false;
                $xjSeatNo = isset($xjinfo['seatNo']) ? intval($xjinfo['seatNo']) : 0;           //庄家座位号；
                $xjRes = CardEnum::dealNiuNiuLevel($xianjCard, $xjLevel);

                $lastWin = 1;  //通过计算谁家赢：1-庄赢，2-闲赢,
                if (!empty($xianjCard) && !empty($xjRes) && $xjLevel >= 0 && $xjSeatNo >= 0) {
                    $xjCardMut = isset($xjRes['mutl_value']) ? $xjRes['mutl_value'] : 1;     //牌型倍数
                    if (isset($xjRes['niuGroups']) && !empty($xjRes['niuGroups'])) {
                        $xianjCard = BaseEnum::changeGroupArray($xianjCard, $xjRes['niuGroups']);
                    }
                    $xjRes['cards'] = $xianjCard;

                    $allCards[$xjSeatNo] = $xianjCard;
                    //比大小：第一步，比KEY值；
                    if ($zjNiuLevel > $xjLevel) {              //牛相比； 庄赢；
                        $lastWin = 1;
                    } else if ($zjNiuLevel < $xjLevel) {        //牛相比； 闲赢；
                        $lastWin = 2;
                    } else if ($zjNiuLevel == $xjLevel) {     //牛相同，比其它； 点数；
                        //牛牛相同时：比较大小；闲赢还是庄赢；
                        $lastWin = CardEnum::gatherWhoWin($xjLevel, $zjRes, $xjRes);
                    }
                    $ifWin = ($lastWin == 1) ? 0 : 1;
                    $seatResult[] = $ifWin;
                    $resultdata["seat{$xjSeatNo}"] = array('seatNo'=>$xjSeatNo,'cards'=>$xianjCard,'level'=>$xjLevel,'card_mutle'=>$xjCardMut,'if_win'=>$ifWin);
                }
            }
            
            if (!empty($seatResult)){
                //TODO 把新的数据加到列表中
                PlayHelp::getInstance()->setResultLog($gameId,"newhistory_".$roomId,$seatResult);  
                
                //TODO 加入列表
                $historyWins = PlayHelp::getInstance()->getResultLog($roomId,'history');
                if(!$historyWins){ $historyWins  = array([0,0,0,0]);}
                @array_unshift($historyWins,$seatResult);                                   //TODO　array_unshift 插入一条数据到最前面一条；
                if (!empty($historyWins) && sizeof($historyWins) > 11){ @array_pop($historyWins); }
                PlayHelp::getInstance()->setResultLog($roomId,'history',$historyWins);      //保存开奖数据
            }
            
            //手牌信息组合；包含信息：总共多少位置，每个玩家的手牌，庄家位置信息；
            $cardValue = BaseComm::conectUserCardsValue($gameUtype, $fjSeat, $allCards, $zjSeatNo);
            $returndata["cardvalues"] = !empty($cardValue) ? $cardValue : ""; //手牌组合数据
        }
        return $resultdata;
    }
}
?>